from pathlib import Path
from typing import Union

from django.apps.registry import ImproperlyConfigured
from django.contrib.auth.mixins import (
    AccessMixin, LoginRequiredMixin, PermissionRequiredMixin
)

from django.http import HttpResponseRedirect
from django.shortcuts import redirect, reverse
from django.views import View
from django.views.generic import TemplateView
from django.views.generic.edit import ContextMixin, ModelFormMixin

from audit.models import AuditManagement
from audit.lib.views import AuditorContextMixin
from lib.django.views import (
    AuditFormPostAPIView,
    DetailView,
    HTMXCreateFormView,
    HTMXUpdateFormView,
    ReadOnlyFormMixin,
    PermissionCheckMethodMixin
)

from .. import models

TEMPLATE_DIR = Path("app/project/")

def template_path(path: Union[Path, str]) -> str:
    return str(TEMPLATE_DIR / path)

# For one project's child view
class ProjectMixin(ContextMixin):
    project_kwargs_field_name = 'project_pk'

    def get_project(self):
        if project := getattr(self, 'project', None):
            return project
        project_pk = self.kwargs.get(self.project_kwargs_field_name)
        project = models.Project.objects.get(id=project_pk)
        self.project = project
        return project
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["project"] = self.get_project()
        return context
    
    # for form related view
    def form_valid(self, form):
        obj = form.save(commit=False)
        obj.project = self.get_project()
        obj.save()
        form.save_m2m()
        self.object = obj
        return HttpResponseRedirect(self.get_success_url())

    # for PermissionCheckMethodMixin
    def get_check_object(self):
        return self.get_project()


class ProjectSubFormMixin(ProjectMixin, ModelFormMixin):
    # for form related view
    def form_valid(self, form):
        obj = form.save(commit=False)
        obj.project = self.get_project()
        obj.save()
        form.save_m2m()
        self.object = obj
        return HttpResponseRedirect(self.get_success_url())

class HTMXProjectSubCreateFormView(
    LoginRequiredMixin,
    ProjectMixin,
    PermissionCheckMethodMixin,
    HTMXCreateFormView
):
    template_name = template_path("detail_form_base.html")
    create_view_name = None
    detail_view_name = None
    perm_check_func_name = "can_edit"

    def get_form_action_view_name(self):
        project_pk = self.kwargs.get(self.project_kwargs_field_name)
        return reverse(self.create_view_name, kwargs={'project_pk': project_pk})
    
    def get_success_url(self):
        project_pk = self.kwargs.get(self.project_kwargs_field_name)
        pk = getattr(self.object, 'id')
        return reverse(self.detail_view_name, kwargs={'project_pk': project_pk, 'pk': pk})



class HTMXProjectSubCreateFormViewNoPK(HTMXProjectSubCreateFormView):
    def get_success_url(self):
        project_pk = self.kwargs.get(self.project_kwargs_field_name)
        return reverse(self.detail_view_name, kwargs={'project_pk': project_pk})

    
class HTMXProjectSubUpdateFormView(
    LoginRequiredMixin,
    ProjectMixin,
    PermissionCheckMethodMixin,
    HTMXUpdateFormView
):
    template_name = template_path("detail_form_base.html")
    update_view_name = None
    detail_view_name = None
    perm_check_func_name = "can_edit"

    def get_form_action_view_name(self):
        project_pk = self.kwargs.get(self.project_kwargs_field_name)
        pk = self.kwargs.get('pk')
        return reverse(self.update_view_name, kwargs={'project_pk': project_pk, 'pk': pk})
    
    def get_success_url(self):
        project_pk = self.kwargs.get(self.project_kwargs_field_name)
        pk = self.kwargs.get('pk')
        return reverse(self.detail_view_name, kwargs={'project_pk': project_pk, 'pk': pk})

class HTMXProjectSubUpdateFormViewNoPK(HTMXProjectSubUpdateFormView):
    def get_form_action_view_name(self):
        project_pk = self.kwargs.get(self.project_kwargs_field_name)
        return reverse(self.update_view_name, kwargs={'project_pk': project_pk})
    
    def get_success_url(self):
        project_pk = self.kwargs.get(self.project_kwargs_field_name)
        return reverse(self.detail_view_name, kwargs={'project_pk': project_pk})

    def get_object(self, queryset=None):
        project_pk = self.kwargs.get(self.project_kwargs_field_name)
        return self.model.objects.get(project__id=project_pk)


class ProjectSubAuditFormPostAPIView(
    LoginRequiredMixin,
    PermissionRequiredMixin,
    AuditFormPostAPIView
):
    """
    The view inherit from this view should at least define `model` class attribute
    See `AuditFormPostAPIView`.
    """
    permission_required = "user.can_manage_projects"

class ProjectSubAuditFormPostAPIViewNoPK(ProjectSubAuditFormPostAPIView):
    def get_object(self, queryset=None):
        project_pk = self.kwargs.get("project_pk")
        obj = self.model.objects.get(project__id=project_pk)
        return AuditManagement.objects.get_for_object(obj)


class ProjectSubDetailView(
    LoginRequiredMixin,
    ProjectMixin,
    PermissionCheckMethodMixin,
    ReadOnlyFormMixin,
    DetailView
):
    """
    Derived class should at least define these class attributes (example):
    model = models.Examination
    form_class = forms.ExaminationForm
    template_name = template_path("examination/detail.html")
    """
    perm_check_func_name = "can_view"

class ProjectSubDetailViewNoPK(ProjectSubDetailView):
    def get_object(self, queryset=None):
        project_pk = self.kwargs.get(self.project_kwargs_field_name)
        return self.model.objects.get(project__id=project_pk)


class ProjectSubAuditDetailView(AuditorContextMixin, ProjectSubDetailView):
    """
    Should define `audit_viewname`, `audit_permission_required` and those class attributes
    required by `ProjectSubDetailView`
    """
    audit_viewname = None
    
    def get_audit_form_action(self):
        project_pk = self.kwargs.get(self.project_kwargs_field_name)
        pk = self.kwargs.get(self.pk_url_kwarg)
        return reverse(self.audit_viewname, kwargs={'project_pk': project_pk, 'pk': pk})

class ProjectSubAuditDetailViewNoPK(ProjectSubAuditDetailView):
    def get_audit_form_action(self):
        project_pk = self.kwargs.get(self.project_kwargs_field_name)
        return reverse(self.audit_viewname, kwargs={'project_pk': project_pk })

    def get_object(self, queryset=None):
        project_pk = self.kwargs.get(self.project_kwargs_field_name)
        return self.model.objects.get(project__id=project_pk)

    
class ProjectSubListView(
    LoginRequiredMixin,
    ProjectMixin,
    PermissionCheckMethodMixin,
    TemplateView
):
    """
    The subclass should provide `template_name` class attribute
    """
    perm_check_func_name = "can_view"

class ProjectSubIndexRedirectView(
    LoginRequiredMixin,
    ProjectMixin,
    PermissionCheckMethodMixin,
    View
):
    """
    Subclass should set `model`, `detail_view_name` and `create_view_name`
    class attribute.
    """
    perm_check_func_name = "can_view"
    model = None
    detail_view_name = None
    create_view_name = None
    
    
    def get(self, request, *args, **kwargs):
        project_pk = self.kwargs.get(self.project_kwargs_field_name)
        if not self.model:
            raise ImproperlyConfigured(
                f"Class's `model` attribute should be a Non-None value!"
            )
        if self.model.objects.filter(project__id=project_pk).exists():
            return redirect(reverse(
                self.detail_view_name,
                kwargs={'project_pk': project_pk}
            ))
        else:
            return redirect(reverse(
                self.create_view_name,
                kwargs={'project_pk': project_pk}
            ))

